#include "qsafebytearray.h"
#include <new.h>
#include <QMetaProperty>
#include <QThread>
#include <QCoreApplication>
#include <malloc.h>
#include <QDebug>

#ifndef _WIN32_WINNT
	#define _WIN32_WINNT 0x0501
#elif _WIN32_WINNT < 0x0501
	#undef _WIN32_WINNT
	#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>

#define SBA_SAFE_CODE(CODE) try { compactHeap(); CODE; } catch (QSafeByteArray::Exception e) { emit error(e); } catch(...) { emit error(QSafeByteArray::Exception()); }

class CheatThread: public QThread
{
public:
	static void msleep(int msecs)
	{
		QThread::currentThread()->msleep(msecs);
	}
};

struct QSafeByteArray::Data
{
	QByteArray ba;
	HANDLE mem_ntf;
	static QSafeByteArray global_instance;
	Data(): mem_ntf(::CreateMemoryResourceNotification(::HighMemoryResourceNotification))
	{
	}
	~Data()
	{
		::CloseHandle(mem_ntf);
	}
	static int alloc_handler(size_t size) throw(QSafeByteArray::Exception)
	{
		throw QSafeByteArray::Exception(QSafeByteArray::Exception::Allocation, size);
		return 0;
	}
	static size_t compact_heap()
	{
		HANDLE handles[256];
		const DWORD count = ::GetProcessHeaps(sizeof(handles) / sizeof(handles[0]), (PHANDLE)&handles);
		size_t main_heap_largest_block = 0;
		for (DWORD i = 0; i < count; i++)
		{
			size_t largest_block = ::HeapCompact(handles[i], 0);
			if (handles[i] == ::GetProcessHeap())
			{
				main_heap_largest_block = largest_block;
				qDebug() << "heap" << i << "largest block" << largest_block;
			}
		}
		return main_heap_largest_block;
	}
	static void set_heap_info(unsigned long flag)
	{
		HANDLE handles[256];
		const DWORD count = ::GetProcessHeaps(sizeof(handles) / sizeof(handles[0]), (PHANDLE)&handles);
		for (DWORD i = 0; i < count; i++)
		{
			HeapSetInformation(handles[i],
                       HeapCompatibilityInformation,
                       &flag,
                       sizeof(flag));

//			qDebug() << "heap" << i << "largest block" << largest_block;
		}
	}
};

QSafeByteArray QSafeByteArray::Data::global_instance;

void QSafeByteArray::setEnabled(bool _state, QObject * handler, const char * slot)
{
	Data::global_instance.disconnect(SIGNAL(error(QSafeByteArray::Exception)));
	if (_state)
	{
		qRegisterMetaType<QSafeByteArray::Exception>("QSafeByteArray::Exception");

		if (handler && slot)
		{
			QObject::connect(& Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)), handler, slot);
		}
	}

	::_set_new_mode(_state ? 1 : 0);
	::_set_new_handler(_state ? Data::alloc_handler : 0);

	ULONG HeapFragValue = _state ? 2 : 0;
	::HeapSetInformation(::GetProcessHeap(),
						 ::HeapCompatibilityInformation,
						 &HeapFragValue,
						 sizeof(HeapFragValue));
}

bool QSafeByteArray::memoryIsEnough()
{
	BOOL state = false;
	::QueryMemoryResourceNotification(Data::global_instance.d->mem_ntf, &state);
	return state;
}

size_t QSafeByteArray::compactHeap()
{
	return Data::compact_heap();
}

void QSafeByteArray::setHeapInfo(unsigned long flag)
{
	Data::set_heap_info(flag);
}

QSafeByteArray::QSafeByteArray(): QObject(), d(new Data())
{
	QObject::connect(this, SIGNAL(error(QSafeByteArray::Exception)), & Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)));
}

QSafeByteArray::QSafeByteArray ( const char * str ): QObject(), d(new Data())
{
	QObject::connect(this, SIGNAL(error(QSafeByteArray::Exception)), & Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)));
	SBA_SAFE_CODE
	(
		d->ba = QByteArray(str);
	)
}

QSafeByteArray::QSafeByteArray ( const char * data, int size ): QObject() , d(new Data())
{
	QObject::connect(this, SIGNAL(error(QSafeByteArray::Exception)), & Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)));
	SBA_SAFE_CODE
	(
		d->ba = QByteArray(data, size);
	)
}

QSafeByteArray::QSafeByteArray ( int size, char ch ): QObject() , d(new Data())
{
	QObject::connect(this, SIGNAL(error(QSafeByteArray::Exception)), & Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)));
	SBA_SAFE_CODE
	(
		d->ba = QByteArray(size, ch);
	)
}

QSafeByteArray::QSafeByteArray ( int size, Qt::Initialization init ): QObject(), d(new Data())
{
	QObject::connect(this, SIGNAL(error(QSafeByteArray::Exception)), & Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)));
	SBA_SAFE_CODE
	(
		d->ba = QByteArray(size, init);
	)
}

QSafeByteArray::QSafeByteArray ( const QByteArray & other ): QObject() , d(new Data())
{
	QObject::connect(this, SIGNAL(error(QSafeByteArray::Exception)), & Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)));
	SBA_SAFE_CODE
	(
		d->ba = other;
	)
}

QSafeByteArray::QSafeByteArray ( const QSafeByteArray & other ): QObject() , d(new Data())
{
	QObject::connect(this, SIGNAL(error(QSafeByteArray::Exception)), & Data::global_instance, SIGNAL(error(QSafeByteArray::Exception)));
	SBA_SAFE_CODE
	(
		d->ba = other.d->ba;
	)
}

QSafeByteArray::~QSafeByteArray()
{
	delete d;
}

QSafeByteArray & QSafeByteArray::operator=(const QByteArray & other)
{
	SBA_SAFE_CODE
	(
		d->ba = other;
	)
	return * this;
}

QSafeByteArray & QSafeByteArray::operator=(const QSafeByteArray & other)
{
	if (this != & other)
	{
		SBA_SAFE_CODE
		(
			d->ba = other.d->ba;
		)
	}
	return * this;
}

QSafeByteArray & QSafeByteArray::operator=(const char *str)
{
	SBA_SAFE_CODE
	(
		d->ba = QByteArray(str);
	)
	return * this;
}

QSafeByteArray::operator QByteArray () const
{
	return d->ba;
}

QByteArray & QSafeByteArray::byteArray() const
{
	return d->ba;
}

